package com.dj.mingkong.admin.biz.scheduler;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dj.mingkong.admin.biz.enums.SyncStatusEnum;
import com.dj.mingkong.admin.biz.enums.SyncTypeEnum;
import com.dj.mingkong.admin.biz.repository.database.music.MusicRepository;
import com.dj.mingkong.admin.biz.repository.database.system.SyncLogRepository;
import com.dj.mingkong.admin.biz.repository.database.system.UserRepository;
import com.dj.mingkong.admin.biz.repository.elasticsearch.music.MusicEsRepository;
import com.dj.mingkong.admin.biz.repository.mongodb.system.UserMgRepository;
import com.dj.mingkong.common.enums.DeleteEnum;
import com.dj.mingkong.common.infrastructure.base.eo.BaseEo;
import com.dj.mingkong.common.infrastructure.eo.database.system.SyncLogEo;
import com.dj.mingkong.common.infrastructure.eo.elasticsearch.music.MusicEsEo;
import com.dj.mingkong.common.infrastructure.eo.mongodb.system.UserMgEo;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

/**
 * @author zenglonghui
 * @date: 2023/7/17 15:56
 */
@Slf4j
@Component
public class SyncDataJob {
    @Resource
    private SyncLogRepository syncLogRepository;
    @Resource
    private UserRepository userRepository;
    @Resource
    private UserMgRepository userMgRepository;
    @Resource
    private MusicRepository musicRepository;
    @Resource
    private MusicEsRepository musicEsRepository;

    private static final long SLEEP_TIME = 1000;
    private static final long PAGE_SIZE = 100;

    /**
     * 同步用户到mongodb
     */
    @Transactional(rollbackFor = Exception.class)
    @XxlJob("syncMgUserJob")
    public void syncMgUserJob() {
        XxlJobHelper.log("syncMgUserJob start......");
        log.info("syncMgUserJob start......");
        final LocalDateTime syncTime = getSyncTime(SyncTypeEnum.MG_USER.getIntVal());
        log.info("syncMgUserJob syncTime: {}", Objects.isNull(syncTime) ? "null" : syncTime);
        final SyncLogEo syncLogEo = insertSyncLog(SyncTypeEnum.MG_USER.getIntVal());
        try {
            boolean flag = true;
            long page = 1;
            while (flag) {
                final List<UserMgEo> userEoList = getUserData(page, syncTime);
                log.info("syncMgUserJob page: {}, userEoList.size: {}", page, userEoList.size());
                if (CollectionUtils.isEmpty(userEoList)) {
                    flag = false;
                } else {
                    syncMgUserData(userEoList, syncLogEo);
                    page++;
                    Thread.sleep(SLEEP_TIME);
                }
            }
            syncLogEo.setEndTime(LocalDateTime.now()).setMsg(SyncStatusEnum.SYNC_SUCCESS.getDesc())
                    .setStatus(SyncStatusEnum.SYNC_SUCCESS.getIntVal());
            syncLogRepository.updateById(syncLogEo);
            log.info("syncMgUserJob insertCount: {}, updateCount: {}, deleteCount: {}",
                    syncLogEo.getInsertCount(), syncLogEo.getUpdateCount(), syncLogEo.getDeleteCount());
        } catch (Exception e) {
            XxlJobHelper.log("syncMgUserJob error", e);
            log.error("syncMgUserJob error", e);
            syncLogEo.setEndTime(LocalDateTime.now()).setMsg(e.getMessage())
                    .setStatus(SyncStatusEnum.SYNC_ERROR.getIntVal());
            syncLogRepository.updateById(syncLogEo);
        }
        XxlJobHelper.log("syncMgUserJob end......");
        log.info("syncMgUserJob end......");
    }

    /**
     * 同步音乐到elasticsearch
     */
    @XxlJob("syncEsMusicJob")
    public void syncEsMusicJob() {
        XxlJobHelper.log("syncEsMusicJob start......");
        log.info("syncEsMusicJob start......");
        final LocalDateTime syncTime = getSyncTime(SyncTypeEnum.ES_MUSIC.getIntVal());
        log.info("syncEsMusicJob syncTime: {}", Objects.isNull(syncTime) ? "null" : syncTime);
        final SyncLogEo syncLogEo = insertSyncLog(SyncTypeEnum.ES_MUSIC.getIntVal());
        try {
            boolean flag = true;
            long page = 1;
            while (flag) {
                final List<MusicEsEo> musicEsEoList = getMusicData(page, syncTime);
                log.info("syncEsMusicJob page: {}, musicEsEoList.size: {}", page, musicEsEoList.size());
                if (CollectionUtils.isEmpty(musicEsEoList)) {
                    flag = false;
                } else {
                    syncEsMusicData(musicEsEoList, syncLogEo);
                    page++;
                    Thread.sleep(SLEEP_TIME);
                }
            }
            syncLogEo.setEndTime(LocalDateTime.now()).setMsg(SyncStatusEnum.SYNC_SUCCESS.getDesc())
                    .setStatus(SyncStatusEnum.SYNC_SUCCESS.getIntVal());
            syncLogRepository.updateById(syncLogEo);
            log.info("syncEsMusicJob insertCount: {}, updateCount: {}, deleteCount: {}",
                    syncLogEo.getInsertCount(), syncLogEo.getUpdateCount(), syncLogEo.getDeleteCount());
        } catch (Exception e) {
            XxlJobHelper.log("syncEsMusicJob error", e);
            log.error("syncEsMusicJob error", e);
            syncLogEo.setEndTime(LocalDateTime.now()).setMsg(e.getMessage())
                    .setStatus(SyncStatusEnum.SYNC_ERROR.getIntVal());
            syncLogRepository.updateById(syncLogEo);
        }
        XxlJobHelper.log("syncEsMusicJob end......");
        log.info("syncEsMusicJob end......");
    }

    private void syncEsMusicData(List<MusicEsEo> musicEsEoList, SyncLogEo syncLogEo) {
        for (MusicEsEo musicEsEo : musicEsEoList) {
            if (musicEsRepository.existsById(musicEsEo.getId())) {
                if (DeleteEnum.NOT.equalsIntVal(musicEsEo.getDeleted())) {
                    musicEsRepository.save(musicEsEo);
                    syncLogEo.setUpdateCount(syncLogEo.getUpdateCount() + 1);
                } else {
                    musicEsRepository.deleteById(musicEsEo.getId());
                    syncLogEo.setDeleteCount(syncLogEo.getDeleteCount() + 1);
                }
            } else {
                if (DeleteEnum.NOT.equalsIntVal(musicEsEo.getDeleted())) {
                    musicEsRepository.save(musicEsEo);
                    syncLogEo.setInsertCount(syncLogEo.getInsertCount() + 1);
                }
            }
        }
    }

    private List<MusicEsEo> getMusicData(long page, LocalDateTime syncTime) {
        return musicRepository.getMapper().listPage(Page.of(page, PAGE_SIZE), syncTime).getRecords();
    }

    private void syncMgUserData(List<UserMgEo> userEoList, SyncLogEo syncLogEo) {
        for (UserMgEo userEo : userEoList) {
            if (userMgRepository.existsById(userEo.getId())) {
                if (DeleteEnum.NOT.equalsIntVal(userEo.getDeleted())) {
                    userMgRepository.save(userEo);
                    syncLogEo.setUpdateCount(syncLogEo.getUpdateCount() + 1);
                } else {
                    userMgRepository.deleteById(userEo.getId());
                    syncLogEo.setDeleteCount(syncLogEo.getDeleteCount() + 1);
                }
            } else {
                if (DeleteEnum.NOT.equalsIntVal(userEo.getDeleted())) {
                    userMgRepository.save(userEo);
                    syncLogEo.setInsertCount(syncLogEo.getInsertCount() + 1);
                }
            }
        }
    }

    private List<UserMgEo> getUserData(long page, LocalDateTime syncTime) {
        return userRepository.getMapper().listPage(Page.of(page, PAGE_SIZE), syncTime).getRecords();
    }

    private SyncLogEo insertSyncLog(Integer type) {
        final SyncLogEo syncLogEo = SyncLogEo.builder().type(type)
                .startTime(LocalDateTime.now()).endTime(LocalDateTime.now())
                .status(SyncStatusEnum.SYNCING.getIntVal()).msg(SyncStatusEnum.SYNCING.getDesc()).build();
        syncLogRepository.save(syncLogEo);
        return syncLogRepository.getById(syncLogEo.getId());
    }

    private LocalDateTime getSyncTime(Integer type) {
        final List<SyncLogEo> syncLogEos = syncLogRepository.lambdaQuery()
                .eq(SyncLogEo::getStatus, SyncStatusEnum.SYNC_SUCCESS.getIntVal())
                .eq(SyncLogEo::getType, type)
                .orderByDesc(BaseEo::getUpdateTime)
                .page(Page.of(1, 1)).getRecords();
        return CollectionUtils.isEmpty(syncLogEos) ? null : syncLogEos.get(0).getCreateTime();
    }
}
